﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Collections;
using System.Reflection;
using MolineSoftware.DrupalXmlRpc.Interfaces;
using MolineSoftware.DrupalXmlRpc.Classes;
using CookComputing.XmlRpc;

namespace MolineSoftware.DrupalXmlRpc
{
    /// <summary>
    /// Class: Contains extention methos for <see cref="XmlRpcStruct"/>
    /// </summary>
    public static class XmlRpcStructExtentionMethods
    {
        public static string ToValue(this XmlRpcStruct[] xnode)
        {
            try
            {
                XmlRpcStruct firstX = xnode[0];
                object value= null;

                if (firstX["value"].GetType() == typeof(string))
                    value = firstX.GetValue("value");
                else if (firstX["value"].GetType() == typeof(XmlRpcStruct[]))
                {
                    value = firstX.GetXmlRpcStructArrayValue("value");
                    return ((XmlRpcStruct[])value)[0]["value"].ToString();
                }
                else if (firstX["value"].GetType() == typeof(string[]))
                    value = firstX.GetXmlRpcStructArrayValue("value");


                var rvalue = (from p in firstX.Cast<DictionaryEntry>() //values
                              where p.Key.ToString().ToUpper() == "VALUE"
                              select p).Single();

                return rvalue.Value.ToString();
         

            }
            catch (Exception ex)
            {                
                throw ex;
            }
        }

        /// <summary>
        /// Checks to see if <see cref="XmlRpcStruct"/> contains a value for the label.
        /// </summary>
        /// <param name="xml">The <see cref="XmlRpcStruct"/> to check.</param>
        /// <param name="label">The name of the <see cref="XmlRpcStruct"/> </param>
        /// <returns>true or false</returns>
        public static new bool ContainsLabelValue(this XmlRpcStruct xml, string label)
        {
            try
            {
                if (xml[label.Replace("#","")].ToString().Length >= 1)
                    return true;
                else
                    return false;
            }
            catch(Exception ex)
            {
                return false;              
            }

        }

        /// <summary>
        /// Gets the value based on label.
        /// </summary>
        /// <param name="xml"></param>
        /// <param name="label"></param>
        /// <returns></returns>
        public static string GetValue(this XmlRpcStruct xml, string label)
        {
            string sValue = xml[label.Replace("#","")].ToString();
            string rValue = string.Empty;
            if (sValue == "System.Object[]")
            {
                try
                {
                    rValue = xml.GetViewSubValue("value", label).ToString();
                   
                }
                catch (Exception ex)
                {
                   // return xml.GetViewSubValueObject("value", label).ToString();
                }
               
            }
            else if (sValue == "CookComputing.XmlRpc.XmlRpcStruct")
            {
                return null;
            }

            if (string.IsNullOrEmpty(rValue))
                return sValue;

            return rValue;

        }
        
        /// <summary>
        /// Gets the value based on label.
        /// </summary>
        /// <param name="xml"></param>
        /// <param name="label"></param>
        /// <returns></returns>
        public static XmlRpcStruct[] GetXmlRpcStructArrayValue(this XmlRpcStruct xml, string label)
        {
            return xml.GetViewSubValueObject("value", label);
    
        }

        public static XmlRpcStruct GetXmlRpcStructValue(this XmlRpcStruct xml, string label)
        {
            
            var rvalue = (from p in xml.Cast<DictionaryEntry>() 
                          where p.Key.ToString() == label.Replace("#","")
                          select p).Single();
            var v1 = rvalue.Value as XmlRpcStruct;          

            return v1;
        }
    
        public static string GetViewSubValue(this XmlRpcStruct rpc, string label, string valueLabel)
        {
            try
            {

                //if (valueLabel == "title")
                //{
                //    string title = (from p in rpc.Cast<DictionaryEntry>()//values
                //                    where p.Key.ToString() == valueLabel
                //                    select new { Value = p.Value }).Single().Value.ToString();
                //    return title;
                //}

                var rvalue = (from p in rpc.Cast<DictionaryEntry>() //values
                              where p.Key.ToString() == valueLabel
                              select p).Single();


                XmlRpcStruct xValue = rvalue.Value as XmlRpcStruct;
                if (xValue == null) //CHANGED: 01/14/2010
                {
                    xValue = (from p in rpc.Cast<DictionaryEntry>()
                              where p.Key.ToString() == valueLabel
                              select new
                              {
                                  KeyValuePair = (object[])rvalue.Value
                              }).Single().KeyValuePair.Cast<XmlRpcStruct>().Single();

                }
                return xValue[label].ToString();
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        public static bool HasValue(this XmlRpcStruct rpc, string label, string valueLabel)
        {
            try
            {
                var rvalue = (from p in rpc.Cast<DictionaryEntry>() //values
                              where p.Key.ToString() == valueLabel
                              select p).Single();

                var xValue = (from p in rpc.Cast<DictionaryEntry>()
                              where p.Key.ToString() == valueLabel
                              select new
                              {
                                  KeyValuePair = (object[])rvalue.Value
                              }).Single().KeyValuePair.Cast<XmlRpcStruct>().Single();
                return true;
            }
            catch 
            {
                return false;
            }
        }

        public static XmlRpcStruct[] GetViewSubValueObject(this XmlRpcStruct rpc, string label, string valueLabel)
        {
            try
            {
                //var values = rpc.Cast<DictionaryEntry>();
                var rvalue = (from p in rpc.Cast<DictionaryEntry>() //values
                              where p.Key.ToString() == valueLabel.Replace("#", "")
                              select p).Single();

                //var xValue = rvalue.Value as XmlRpcStruct[];
                //return xValue;
              

                XmlRpcStruct[] xValue2 = (from p in rpc.Cast<DictionaryEntry>()
                                          where p.Key.ToString() == valueLabel.Replace("#", "")
                                          select new {
                                              KeyValuePair = (object[])rvalue.Value
                                          }).Single().KeyValuePair.Cast<XmlRpcStruct>().ToArray();
                return xValue2;


            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        ///// <summary>
        ///// Add new value to <see cref="XmlRpcStruct"/> values.
        ///// </summary>
        ///// <typeparam name="T">The </typeparam>
        ///// <param name="rpc">The value list.</param>
        ///// <param name="label">The label of the value</param>
        ///// <param name="value">The value to add</param>
        //public static void AddValue(this IList<XmlRpcStruct> rpc, string label, string value)
        //{
        //    if (rpc == null)
        //        rpc = new XmlRpcStruct[1];

        //    if (rpc[0] == null)
        //        rpc[0] = new XmlRpcStruct();

        //    rpc[0].Add(label, value);

        //}

        /// <summary>
        /// Add value to <see cref="XmlRpcStruct"/>
        /// </summary>
        /// <param name="rpc"></param>
        /// <param name="value">The value to add to <see cref="XmlRpcStruct"/></param>
        public static void AddValue(this IList<XmlRpcStruct> rpc, string value)
        {
            AddValue(rpc, value, "value");
        }

        /// <summary>
        /// Add value to <see cref="XmlRpcStruct"/>
        /// </summary>
        /// <param name="rpc"></param>
        /// <param name="value">The value to add to <see cref="XmlRpcStruct"/></param>
        public static void AddValue(this IList<XmlRpcStruct> rpc, string value, string label)
        {
            DictionaryEntry de = new DictionaryEntry(label, value);
            rpc[0] = new XmlRpcStruct();
            rpc[0].Add(de.Key, de.Value);

        }

        /// <summary>
        /// Convert to base: <see cref="DrupalNode"/>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="s"></param>
        /// <returns></returns>
        public static T Convert<T>(this XmlRpcStruct s) where T : DrupalBase
        {
            T node = Activator.CreateInstance<T>();
            try
            {
                 foreach (PropertyInfo pi in node.GetType().GetProperties())
                {
                    try
                    {
                        XmlRpcMemberAttribute att =
                            Attribute.GetCustomAttribute(pi, typeof(XmlRpcMemberAttribute)) as XmlRpcMemberAttribute;

                        if (att == null)
                            continue;

                        if (!s.ContainsLabelValue(att.Member))
                            continue;

                        object value = null;

                        //Check to see if item has a value.
                        if (s[att.Member].GetType() == typeof(object[]))
                        {
                            if (!s.HasValue("value", att.Member))
                                continue;
                        }

                        if (s[att.Member].GetType() == typeof(string))
                            value = s.GetValue(att.Member);
                        else if (s[att.Member].GetType() == typeof(object[]))
                            value = s.GetXmlRpcStructArrayValue(att.Member);
                        else if (s[att.Member].GetType() == typeof(string[]))
                            value = s.GetXmlRpcStructArrayValue(att.Member);

                        #region | OLD CoDE 02/11/2010 |
                        //try
                        //{
                        //    //First try to get value as a string. If Error
                        //    value = s.GetValue(att.Member);
                        //}
                        //catch { value = s.GetXmlRpcStructArrayValue(att.Member); }

                        //if (value == null || value.ToString() == "System.Object[]") //not a string value
                        //{
                        //    value = s.GetXmlRpcStructArrayValue(att.Member);

                        //    if (value == null) //not an Array value;
                        //    {
                        //        value = s.GetXmlRpcStructValue(att.Member);
                        //    }
                        //}
                        #endregion

                        node.SetValue<T>(pi.Name, value, "value");

                        #region | OLD CODE 10-09-2009
                        //TODO: DELETE ME
                        //try
                        //{
                        //    object value = s.GetValue(att.Member);
                        //    if (value == null) //not a string value
                        //    {
                        //        value = s.GetXmlRpcStructArrayValue(att.Member);
                        //        if (value == null) //not an Array value;
                        //        {
                        //            value = s.GetXmlRpcStructValue(att.Member);
                        //        }
                        //    }
                        //    node.SetValue<T>(pi.Name, value, "value");
                        //}
                        //catch
                        //{
                        //    try
                        //    {
                        //        object value2 = s.GetXmlRpcStructArrayValue(att.Member);
                        //        if (value2 == null)
                        //        {
                        //            value2 = s.GetXmlRpcStructValue(att.Member);
                        //        }

                        //        node.SetValue<T>(pi.Name, value2, "value");
                        //    }
                        //    catch (Exception ex)
                        //    {
                        //        throw ex;
                        //    }
                        //}


                        #endregion
                    
                    } //TODO: Handel Error
                    catch (Exception ex) { }
                }

                #region | Figure out out to set sub XmlRpcStructions like Drupal |
                 //int pcount = node.GetType().GetProperties().Count();
                 //pcount = node.GetType().GetNestedTypes().Count();
                 //pcount = node.GetType().GetFields().Count();
                //foreach (FieldInfo fi in node.GetType().GetFields())
                //{
                //    try
                //    {
                //        XmlRpcMemberAttribute att =
                //            Attribute.GetCustomAttribute(fi, typeof(XmlRpcMemberAttribute)) as XmlRpcMemberAttribute;

                //        if (att == null)
                //            continue;

                //        Type t = s[att.Member].GetType();
                //        XmlRpcStruct value = s[att.Member] as XmlRpcStruct;

                //        T type = Activator.CreateInstance<T>();
                //        type = value.Convert<T>();

                //        //node.SetValue<T>(fi.Name, value, "value");

                //        //fi.SetValue(node, value.Convert<T>());


                //    }
                //    catch (Exception ex)
                //    {

                //    }

                //}
                #endregion
                
               
                return node;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Convert to base: <see cref="DrupalNode"/>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="s"></param>
        /// <returns></returns>
        public static List<T> Convert<T>(this XmlRpcStruct[] s) where T : DrupalBase
        {
            //T node = Activator.CreateInstance<T>();
            try
            {
                List<T> tList = Activator.CreateInstance<List<T>>();
                foreach (XmlRpcStruct rs in s)
                {
                    T node = Activator.CreateInstance<T>();
                    foreach (PropertyInfo pi in node.GetType().GetProperties())
                    {
                        try
                        {
                            XmlRpcMemberAttribute att =
                                Attribute.GetCustomAttribute(pi, typeof(XmlRpcMemberAttribute)) as XmlRpcMemberAttribute;

                            if (att == null)
                                continue;

                            if (!rs.ContainsLabelValue(att.Member))
                                continue;

                            //TODO: This is a hack and needs to be fixed for array[] values.
                            if (att.Member == "#args")
                            {
                                try
                                {
                                    object value2 = rs.GetXmlRpcStructArrayValue(att.Member);
                                    
                                    if (value2 == null)
                                    {
                                        value2 = rs.GetXmlRpcStructValue(att.Member);
                                    }
                                    node.SetValue<T>(pi.Name, value2, "value");
                                } //TODO: Handle Error
                                catch (Exception ex)  {   }
                            }
                            else
                            {
                                object value = rs.GetValue(att.Member);
                                node.SetValue<T>(pi.Name, value, "value");
                            }
                           
                        }//TODO: Handle error
                        catch (Exception ex) { }
                    }
                    tList.Add(node);
                }



                return tList;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Convert to base: <see cref="DrupalNode"/>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="s"></param>
        /// <returns></returns>
        public static IEnumerable<T> IConvert<T>(this XmlRpcStruct[] s) where T : DrupalBase
        {
            //T node = Activator.CreateInstance<T>();
            try
            {
                List<T> tList = Activator.CreateInstance<List<T>>();
                foreach (XmlRpcStruct rs in s)
                {
                    T node = Activator.CreateInstance<T>();
                    foreach (PropertyInfo pi in node.GetType().GetProperties())
                    {
                        try
                        {
                            XmlRpcMemberAttribute att =
                                Attribute.GetCustomAttribute(pi, typeof(XmlRpcMemberAttribute)) as XmlRpcMemberAttribute;

                            if (att == null)
                                continue;

                            if (!rs.ContainsLabelValue(att.Member))
                                continue;

                            object value = rs.GetValue(att.Member);
                            if (value == null) //not a string value
                            {
                                value = rs.GetXmlRpcStructArrayValue(att.Member);
                                if (value == null) //not an Array value;
                                {
                                    value = rs.GetXmlRpcStructValue(att.Member);
                                }
                            }
                            node.SetValue<T>(pi.Name, value, "value");

                        } //TODO: Handle ERROR
                        catch (Exception ex) { }
                    }
                    tList.Add(node);
                }
                return tList;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Convert to base: <see cref="DrupalUser"/>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="s"></param>
        /// <returns></returns>
        public static T ConvertUser<T>(this XmlRpcStruct s) where T : DrupalUser
        {
            T node = Activator.CreateInstance<T>();
            try
            {
                foreach (PropertyInfo pi in node.GetType().GetProperties())
                {
                    try
                    {
                        XmlRpcMemberAttribute att =
                            Attribute.GetCustomAttribute(pi, typeof(XmlRpcMemberAttribute)) as XmlRpcMemberAttribute;

                        if (att == null)
                            continue;


                        if (!s.ContainsLabelValue(att.Member))
                            continue;

                        object value = s.GetValue(att.Member);
                        if (value == null) //not a string value
                        {
                            value = s.GetXmlRpcStructArrayValue(att.Member);
                            if (value == null) //not an Array value;
                            {
                                value = s.GetXmlRpcStructValue(att.Member);
                            }
                        }
                        node.SetValue<T>(pi.Name, value, "value");

                    } //TODO: Handle Error
                    catch (Exception ex) { }
                }
                return node;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}
